package com.stellar.circle.handler;


import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.stellar.circle.basic.entity.SChatMessage;
import com.stellar.circle.basic.service.SChatMessageService;
import com.stellar.circle.domain.sChatMessage.SChatMessageDomainService;
import com.stellar.circle.domain.sChatRelationship.SChatRelationshipDomainService;
import com.stellar.circle.domain.sChatSensitiveLog.SChatSensitiveLogDomainService;
import com.stellar.circle.utils.SensitiveWordUtil;
import org.apache.hc.core5.http.HttpStatus;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 当前方案只适合单机部署，集群部署时需要使用Redis存储session信息
 */
@Component
public class WebSocketHandler extends TextWebSocketHandler {

    // 存储所有在线的session
    private static final Map<Long, WebSocketSession> sessions = new ConcurrentHashMap<>();

    // 心跳超时时间（毫秒）
    private static final long HEARTBEAT_TIMEOUT = 30000;

    // 存储最后活动时间
    private static final Map<Long, Long> lastActivityMap = new ConcurrentHashMap<>();

    @Resource
    private SChatMessageDomainService sChatMessageDomainService;

    @Resource
    private SChatRelationshipDomainService sChatRelationshipDomainService;

    @Resource
    private SChatSensitiveLogDomainService sChatSensitiveLogDomainService;

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        Long userId = (Long) session.getAttributes().get("userId");
        if (userId == null) {
            session.close();
            return;
        }

        // 更新最后活动时间
        lastActivityMap.put(userId, System.currentTimeMillis());

        // 处理心跳消息
        if (isHeartbeatMessage(message)) {
            // 响应Pong消息
            session.sendMessage(new PongMessage());
            return;
        }

        SChatMessage messageVO = JSON.parseObject(message.getPayload(), SChatMessage.class);

        // 检查是否被拉黑
        if (sChatRelationshipDomainService.isBlocked(userId, messageVO.getReceiverId())) {
            sendError(session, "对方已拉黑您");
            return;
        }

        // 敏感词过滤
        SensitiveWordUtil.SensitiveResult result = SensitiveWordUtil.filter(messageVO.getContent());
        if (result.isSensitive()) {
            sChatSensitiveLogDomainService.handleSensitiveMessage(userId, messageVO, result.getWords());
            sendError(session, "消息包含敏感词");
            return;
        }

        // 保存消息到数据库
        SChatMessage savedMsg = sChatMessageDomainService.saveMessage(messageVO);

        // 直接发送给接收者
        sendToUser(savedMsg.getReceiverId(), JSON.toJSONString(savedMsg));
    }

    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) {
        // 收到Pong响应，更新最后活动时间
        Long userId = (Long) session.getAttributes().get("userId");
        if (userId != null) {
            lastActivityMap.put(userId, System.currentTimeMillis());
        }
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        Long userId = (Long) session.getAttributes().get("userId");
        if (userId != null) {
            sessions.put(userId, session);
            lastActivityMap.put(userId, System.currentTimeMillis());

            // 发送离线消息
//            sChatMessageService.sendOfflineMessages(userId);
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        Long userId = (Long) session.getAttributes().get("userId");
        if (userId != null) {
            closeSession(userId, session);
        }
    }

    // 发送消息给指定用户
    public boolean sendToUser(Long receiverId, String message) {
        WebSocketSession session = sessions.get(receiverId);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(message));
            } catch (IOException e) {
                // 发生异常时关闭会话
                closeSession(receiverId, session);
            }
        } else {
            // 用户不在线，消息存为离线消息（在数据库标记为未读）
            assert session != null;
            Long userId = (Long) session.getAttributes().get("userId");
            SChatMessage sChatMessage = new SChatMessage();
            sChatMessage.setSenderId(userId);
            sChatMessage.setReceiverId(receiverId);
            sChatMessage.setContent(message);
            sChatMessageDomainService.saveMessage(sChatMessage);
        }
        return false;
    }

    /**
     * 关闭会话
     *
     * @param userId  用户ID
     * @param session 会话
     */
    private void closeSession(Long userId, WebSocketSession session) {
        try {
            if (session.isOpen()) {
                session.close();
            }
        } catch (IOException e) {
            // 关闭异常处理
        } finally {
            sessions.remove(userId);
            lastActivityMap.remove(userId);
        }
    }

    /**
     * 判断是否是心跳消息
     *
     * @param message 消息
     * @return
     */
    private boolean isHeartbeatMessage(TextMessage message) {
        try {
            String payload = message.getPayload();
            Map<?, ?> data = JSON.parseObject(payload, Map.class);
            return "heartbeat".equals(data.get("type"));
        } catch (Exception e) {
            return false;
        }
    }


    private void sendError(WebSocketSession session, String error) throws IOException {
        Map<String, Object> errorMsg = new HashMap<>();
        errorMsg.put("type", "error");
        errorMsg.put("code", HttpStatus.SC_FORBIDDEN);
        errorMsg.put("content", error);
        session.sendMessage(new TextMessage(JSONUtil.toJsonStr(errorMsg)));
    }

    @Scheduled(fixedRate = 10000)
    public void checkHeartbeat() {
        long currentTime = System.currentTimeMillis();
        sessions.forEach((userId, session) -> {
            Long lastActivity = lastActivityMap.getOrDefault(userId, 0L);
            if (currentTime - lastActivity > HEARTBEAT_TIMEOUT) {
                try {
                    // 发送Ping消息
                    session.sendMessage(new PingMessage());
                } catch (IOException e) {
                    closeSession(userId, session);
                }
            }
        });
    }

}